package com.example.bm.common.helper;

import com.alibaba.fastjson.JSONObject;
import com.example.bm.generator.pojo.Column;
import com.example.common.constant.GeneratorConstant;
import com.example.common.util.StringUtil;
import com.example.dao.model.TGeneratorConfig;
import com.google.common.io.Files;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

/**
 * @author bryceM
 */
@Slf4j
@Component
public class GeneratorHelper {

    /**
     * 生成model
     *
     * @param columns
     * @param configure
     * @throws Exception
     */
    public void generateEntityFile(List<Column> columns, TGeneratorConfig configure) throws Exception {
        String path = getFilePath(configure, configure.getEntityPackage(), GeneratorConstant.JAVA_FILE_SUFFIX, false);
        JSONObject data = toJSONObject(configure);
        data.put("hasDate", false);
        data.put("hasBigDecimal", false);
        columns.forEach(c -> {
            c.setField(StringUtil.underscoreToCamel(StringUtils.lowerCase(c.getName())));
            if (StringUtils.containsAny(c.getType(), "date", "datetime", "timestamp")) {
                data.put("hasDate", true);
            }
            if (StringUtils.containsAny(c.getType(), "decimal", "numeric")) {
                data.put("hasBigDecimal", true);
            }
        });
        data.put("columns", columns);
        this.generateFileByTemplate(GeneratorConstant.ENTITY_TEMPLATE, new File(path), data);
    }

    /**
     * 生成 mapper 接口文件
     *
     * @param columns
     * @param configure
     * @throws Exception
     */
    public void generateMapperFile(List<Column> columns, TGeneratorConfig configure) throws Exception {
        String path = getFilePath(configure, configure.getMapperPackage(), GeneratorConstant.MAPPER_FILE_SUFFIX, false);
        generateFileByTemplate(GeneratorConstant.MAPPER_TEMPLATE, new File(path), toJSONObject(configure));
    }

    /**
     * 生成 service
     *
     * @param columns
     * @param configure
     * @throws Exception
     */
    public void generateServiceFile(List<Column> columns, TGeneratorConfig configure) throws Exception {
        String path = getFilePath(configure, configure.getServicePackage(), GeneratorConstant.SERVICE_FILE_SUFFIX, true);
        generateFileByTemplate(GeneratorConstant.SERVICE_TEMPLATE, new File(path), toJSONObject(configure));
    }

    /**
     * 生成serice impl
     *
     * @param columns
     * @param configure
     * @throws Exception
     */
    public void generateServiceImplFile(List<Column> columns, TGeneratorConfig configure) throws Exception {
        String path = getFilePath(configure, configure.getServiceImplPackage(), GeneratorConstant.SERVICEIMPL_FILE_SUFFIX, false);
        generateFileByTemplate(GeneratorConstant.SERVICEIMPL_TEMPLATE,  new File(path), toJSONObject(configure));
    }

    /**
     * 生成controller
     *
     * @param columns
     * @param configure
     * @throws Exception
     */
    public void generateControllerFile(List<Column> columns, TGeneratorConfig configure) throws Exception {
        String path = getFilePath(configure, configure.getControllerPackage(), GeneratorConstant.CONTROLLER_FILE_SUFFIX, false);
        generateFileByTemplate(GeneratorConstant.CONTROLLER_TEMPLATE, new File(path), toJSONObject(configure));
    }

    /**
     * 生成 xml 文件
     *
     * @param columns
     * @param configure
     * @throws Exception
     */
    public void generateMapperXmlFile(List<Column> columns, TGeneratorConfig configure) throws Exception {
        String path = getFilePath(configure, configure.getMapperXmlPackage(), GeneratorConstant.MAPPERXML_FILE_SUFFIX, false);
        File mapperXmlFile = new File(path);
        JSONObject data = toJSONObject(configure);
        columns.forEach(c -> c.setField(StringUtil.underscoreToCamel(StringUtils.lowerCase(c.getName()))));
        data.put("columns", columns);
        generateFileByTemplate(GeneratorConstant.MAPPERXML_TEMPLATE, mapperXmlFile, data);
    }

    @SuppressWarnings("UnstableApiUsage")
    private void generateFileByTemplate(String templateName, File file, Object data) throws Exception {
        Template template = getTemplate(templateName);
        Files.createParentDirs(file);
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        try (Writer out = new BufferedWriter(new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8), 10240)) {
            template.process(data, out);
        } catch (Exception e) {
            String message = "代码生成异常";
            log.error(message, e);
            throw new Exception(message);
        }
    }


    /**
     * 获取公共路径
     *
     * @param configure
     * @return
     */
    public String genPrefixPath(TGeneratorConfig configure) {
        String filePath = "";
        if (StringUtils.isBlank(configure.getGenPath())) {
            filePath = GeneratorConstant.TEMP_PATH;
        } else {
            filePath = configure.getGenPath();
        }
        //加上 File.separator;
        if (!filePath.endsWith(File.separator)) {
            filePath += File.separator;
        }
        return filePath + System.currentTimeMillis() + File.separator;
    }

    /**
     * 文件临时目录
     *
     * @param configure
     * @param packagePath
     * @param suffix
     * @param serviceInterface
     * @return
     */
    private String getFilePath(TGeneratorConfig configure, String packagePath, String suffix, boolean serviceInterface) {
        String filePath = configure.getGenPath() + /* configure.getJavaPath() + */
                packageConvertPath(configure.getBasePackage() + "." + packagePath);
        //接口加 'I' 前缀
        if (serviceInterface) {
            filePath += "I";
        }
        filePath += configure.getClassName() + suffix;
        return filePath;
    }

    private String packageConvertPath(String packageName) {
        return String.format("/%s/", packageName.contains(".") ? packageName.replaceAll("\\.", "/") : packageName);
    }

    private JSONObject toJSONObject(Object o) {
        return JSONObject.parseObject(JSONObject.toJSON(o).toString());
    }

    /**
     * 获取模版
     *
     * @param templateName
     * @return
     * @throws Exception
     */
    private Template getTemplate(String templateName) throws Exception {
        Configuration configuration = new freemarker.template.Configuration(Configuration.VERSION_2_3_23);
        String templatePath = GeneratorHelper.class.getResource(GeneratorConstant.TEMPLATE_PATH).getPath();
        File file = new File(templatePath);
        if (!file.exists()) {
            templatePath = System.getProperties().getProperty("java.io.tmpdir");
            file = new File(templatePath + "/" + templateName);
            FileUtils.copyInputStreamToFile(Objects.requireNonNull(StringUtil.class.getClassLoader().getResourceAsStream("classpath:generator/template/" + templateName)), file);
        }
        configuration.setDirectoryForTemplateLoading(new File(templatePath));
        configuration.setDefaultEncoding("UTF-8");
        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        return configuration.getTemplate(templateName);

    }
}
